package com.flying.acls.domain.service;

import com.flying.acls.dao.AclEntryRepository;
import com.flying.acls.dao.ResourceRepository;
import com.flying.acls.domain.context.ContextStringValueResolver;
import com.flying.acls.domain.context.ExpressionContext;
import com.flying.acls.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.PathMatcher;

import java.util.ArrayList;
import java.util.List;

@Service
public class AclServiceImpl implements AclService {
    @Autowired
    private ResourceRepository resourceRepository;
    @Autowired
    private AclEntryRepository aclEntryRepository;
    @Autowired
    private ContextStringValueResolver resolver;
    @Autowired
    private Matcher matcher;

    @Override
    public boolean isGranted(ExpressionContext context, List<Resource> requiredResources, List<Sid> sids, List<Permission> requiredPermission) {
        for (Resource resource : requiredResources) {
            if (isGranted(context, resource, sids, requiredPermission)) {
                return true;
            }
        }
        return false;
    }

    private boolean isGranted(ExpressionContext context, Resource requiredResource, List<Sid> sids, List<Permission> requiredPermission) {
        List<Resource> resources = readMatchingResources(context, requiredResource);
        for (Resource resource : resources) {
            Iterable<AclEntry> aces = readAces(resource);
            for (Permission p : requiredPermission) {
                for (Sid sid : sids) {
                    for (AclEntry ace : aces) {
                        String patternExpression = ace.getSid().getExpression();
                        if (sid.getExpressionType() == ExpressionType.VAR) {
                            patternExpression = resolver.resolveStringValue(context, patternExpression);
                        }
                        if ((ace.getPermission().getMask() == p.getMask())
                                && (ace.getSid().getExpressionType() == sid.getExpressionType())
                                && (matcher.isPattern(patternExpression) ? matcher.match(patternExpression, sid.getExpression()) : (patternExpression.equals(sid.getExpression())))) {
                            return ace.isGranted();
                        }
                    }
                }
            }
        }
        return false;
    }

    private List<Resource> readMatchingResources(ExpressionContext context, Resource resource) {
        QResource qResource = QResource.resource;
        Iterable<Resource> resources = resourceRepository.findAll(qResource.type.eq(resource.getType()));
        List<Resource> passResources = new ArrayList<>();
        resources.forEach((res) -> {
            String cfgExpression = null;
            switch (res.getExpressionType()) {
                case VAR:
                    cfgExpression = resolver.resolveStringValue(context, res.getExpression());
                    break;
                case DIRECT:
                    cfgExpression = res.getExpression();
                    break;
            }
            if (matcher.match(cfgExpression, resource.getExpression())) passResources.add(res);
        });
        return passResources;
    }

    private Iterable<AclEntry> readAces(Resource resource) {
        QAclEntry qAclEntry = QAclEntry.aclEntry;
        return aclEntryRepository.findAll(qAclEntry.resource.id.eq(resource.getId()));
    }
}
